/************************************************************************
* @Copyright: 2023-2024
* @FileName:
* @Description: Open source mediasoup C++ controller library
* @Version: 1.0.0
* @Author: Jackie Ou
* @CreateTime: 2023-10-30
*************************************************************************/

#pragma once

#include <vector>
#include <atomic>
#include <string>
#include <unordered_map>

#include "sigslot/signal.hpp"
#include "controller/types.h"
#include "controller/rtp_stream.h"
#include "controller/rtp_parameters.h"

namespace srv {

struct ConsumerLayers {
    /**
     * The spatial layer index (from 0 to N).
     */
    int32_t spatialLayer;

    /**
     * The temporal layer index (from 0 to N).
     */
    int32_t temporalLayer;
};

struct ConsumerOptions {
    /**
     * The id of the Producer to consume.
     */
    std::string producerId;

    /**
     * RTP capabilities of the consuming endpoint.
     */
    RtpCapabilities rtpCapabilities;

    /**
     * Whether the Consumer must start in paused mode. Default false.
     *
     * When creating a video Consumer, it's recommended to set paused to true,
     * then transmit the Consumer parameters to the consuming endpoint and, once
     * the consuming endpoint has created its local side Consumer, unpause the
     * server side Consumer using the resume() method. This is an optimization
     * to make it possible for the consuming endpoint to render the video as far
     * as possible. If the server side Consumer was created with paused: false,
     * mediasoup will immediately request a key frame to the remote Producer and
     * suych a key frame may reach the consuming endpoint even before it's ready
     * to consume it, generating “black” video until the device requests a keyframe
     * by itself.
     */
    bool paused = false;

    /**
     * The MID for the Consumer. If not specified, a sequentially growing
     * number will be assigned.
     */
    std::string mid;

    /**
     * Preferred spatial and temporal layer for simulcast or SVC media sources.
     * If unset, the highest ones are selected.
     */
    ConsumerLayers preferredLayers;

    /**
     * Whether this Consumer should enable RTP retransmissions, storing sent RTP
     * and processing the incoming RTCP NACK from the remote Consumer. If not set
     * it's true by default for video codecs and false for audio codecs. If set
     * to true, NACK will be enabled if both endpoints (mediasoup and the remote
     * Consumer) support NACK for this codec. When it comes to audio codecs, just
     * OPUS supports NACK.
     */
    bool enableRtx = true;

    /**
     * Whether this Consumer should ignore DTX packets (only valid for Opus codec).
     * If set, DTX packets are not forwarded to the remote Consumer.
     */
    bool ignoreDtx = false;

    /**
     * Whether this Consumer should consume all RTP streams generated by the
     * Producer.
     */
    bool pipe = false;

    /**
     * Custom application data.
     */
    Json appData;
};

/**
 * Valid types for 'trace' event.
 */
//export type ConsumerTraceEventType = 'rtp' | 'keyframe' | 'nack' | 'pli' | 'fir';

/**
 * 'trace' event data.
 */
struct ConsumerTraceEventData {
    /**
     * Trace type.
     * options:  'rtp' | 'keyframe' | 'nack' | 'pli' | 'fir'
     */
    std::string type;

    /**
     * Event timestamp.
     */
    int64_t timestamp;

    /**
     * Event direction.
     * options: 'in' | 'out'
     */
    std::string direction;

    /**
     * Per type information.
     */
    std::shared_ptr<TraceInfo> info;
};

void to_json(Json &j, const ConsumerTraceEventData &st);

void from_json(const Json &j, ConsumerTraceEventData &st);

struct ConsumerScore {
    /**
     * The score of the RTP stream of the consumer.
     */
    uint8_t score;

    /**
     * The score of the currently selected RTP stream of the producer.
     */
    uint8_t producerScore;

    /**
     * The scores of all RTP streams in the producer ordered by encoding (just
     * useful when the producer uses simulcast).
     */
    std::vector<uint8_t> producerScores;
};

void to_json(Json &j, const ConsumerScore &st);

void from_json(const Json &j, ConsumerScore &st);

using ConsumerStat = RtpStreamSendStats;

void to_json(Json &j, const ConsumerStat &st);

void from_json(const Json &j, ConsumerStat &st);

struct ConsumerInternal {
    std::string routerId;
    std::string transportId;
    std::string consumerId;
};

struct ConsumerData {
    std::string producerId;

    // options: 'audio' | 'video'
    std::string kind;

    RtpParameters rtpParameters;

    // options: 'simple' | 'simulcast' | 'svc' | 'pipe'
    std::string type;
};

struct BaseConsumerDump {
    std::string id;
    std::string producerId;
    std::string kind;
    RtpParameters rtpParameters;
    std::vector<RtpEncodingParameters> consumableRtpEncodings;
    std::vector<uint8_t> supportedCodecPayloadTypes;
    std::vector<std::string> traceEventTypes;
    bool paused;
    bool producerPaused;
    int32_t priority;
};

struct SimpleConsumerDump : BaseConsumerDump {
    std::string type;
    RtpStreamDump rtpStream;
};

struct SimulcastConsumerDump : BaseConsumerDump {
    std::string type;
    RtpStreamDump rtpStream;
    int16_t preferredSpatialLayer;
    int16_t targetSpatialLayer;
    int16_t currentSpatialLayer;
    int16_t preferredTemporalLayer;
    int16_t targetTemporalLayer;
    int16_t currentTemporalLayer;
};

using SvcConsumerDump = SimulcastConsumerDump;

struct PipeConsumerDump : BaseConsumerDump {
    std::string type;
    std::vector<RtpStreamDump> rtpStreams;
};

using ConsumerDump = BaseConsumerDump;

class IConsumerController {
public:
    virtual ~IConsumerController() = default;

    virtual void init() = 0;

    virtual void destroy() = 0;

    virtual const std::string &id() = 0;

    virtual const std::string &producerId() = 0;

    virtual const std::string &kind() = 0;

    virtual const RtpParameters &rtpParameters() = 0;

    virtual const std::string &type() = 0;

    virtual const ConsumerScore &score() = 0;

    virtual const ConsumerLayers &preferredLayers() = 0;

    virtual const ConsumerLayers &currentLayers() = 0;

    virtual void setAppData(const Json &data) = 0;

    virtual const Json &appData() = 0;

    virtual void close() = 0;

    virtual bool closed() = 0;

    virtual std::shared_ptr<ConsumerDump> dump() = 0;

    virtual std::vector<std::shared_ptr<ConsumerStat>> getStats() = 0;

    virtual void pause() = 0;

    virtual void resume() = 0;

    virtual bool paused() = 0;

    virtual bool producerPaused() = 0;

    virtual void setPreferredLayers(const ConsumerLayers &layers) = 0;

    virtual void setPriority(int32_t priority) = 0;

    virtual void unsetPriority() = 0;

    virtual int32_t priority() = 0;

    virtual void requestKeyFrame() = 0;

    // types = 'rtp' | 'keyframe' | 'nack' | 'pli' | 'fir';
    virtual void enableTraceEvent(const std::vector<std::string> &types) = 0;

    virtual void onTransportClosed() = 0;

public:
    // signals
    sigslot::signal<> transportCloseSignal;

    sigslot::signal<> producerCloseSignal;

    sigslot::signal<> producerPauseSignal;

    sigslot::signal<> producerResumeSignal;

    sigslot::signal<const ConsumerScore &> scoreSignal;

    sigslot::signal<const ConsumerLayers &> layersChangeSignal;

    sigslot::signal<const ConsumerTraceEventData &> traceSignal;

    sigslot::signal<const std::vector<uint8_t> &> rtpSignal;

    sigslot::signal<> closeSignal;

    sigslot::signal<> pauseSignal;

    sigslot::signal<> resumeSignal;
};

}
